---
title: Why Immutability
---

import whyImmutability from "/docs/concepts/why_immutability"
import {
  trimSnippet,
  AutoSnippet,
  When,
} from "../../../../../src/components/CodeSnippet";

:::caution
The content of this page may be outdated.  
It will be updated in the future, but for now you may want to refer to the content
in the top of the sidebar instead (introduction/essentials/case-studies/...)
:::

## What is Immutability?

Immutability is when all fields of an `Object` are final or late final.
They are set exactly once upon construction.

Immutability is desireable for many different reasons

- Value equality rather than reference equality
- Local reasoning about a piece of code
  - A far distant piece of code can't obtain a reference and change the object from underneath you
- Easier to reason about for asynchronous and parallel tasks
  - Other code can't mutate your object in between operations
- Safety of APIs
  - What you pass into a method cannot be changed by the callee / caller

A copyWith method helps with reducing verbosity when creating a new object with just a few things changed.

Copying is more efficient than you might think, since dart can reuse any references to sub-objects that have not changed.
:::warning
Make sure your objects are deeply immutable, otherwise you'll have to implement some sort of deep copy mechanism.
:::

## Best Practices

You can use any package you want to create immutable state.

For immutable objects:

- [package:freezed](https://pub.dev/packages/freezed)
- [package:built_value](https://pub.dev/packages/built_value)

For immutable collections (Map, Set, List):

- [package:fast_immutable_collections](https://pub.dev/packages/fast_immutable_collections)
- [package:built_collection](https://pub.dev/packages/built_collection)
- [package:kt_dart](https://pub.dev/packages/kt_dart)
- [package:dartz](https://pub.dev/packages/dartz)

It is highly recommended to use [freezed],
since it has several nice additions beyond just making immutable objects including:

- A generated copyWith method
- Deep copy (copyWith on nested freezed objects)
- Union types
- Union mapping functions

You do not need to use code generation to work with immutable state, but it makes it much easier.

:::warning
If you want to use the built-in collections, make sure to enforce a discipline of making copies of collections when updating them.
The issue with not copying a collection is that riverpod determines whether to emit a new state based on whether the reference to the object has changed.
If you just call a method that mutates an object, the reference is the same.
:::

### Using immutable state

Immutable state is best fit for using a [Notifier] <When codegen={false}> in combination with [NotifierProvider] </When>.
A [Notifier] allows you to expose an interface through which you can 'mutate' the state.
You cannot mutate the state from outside the class you define that extends [Notifier].
This enforces a separation of concerns and keeps business logic outside of your UI.

Here is an example of a simple immutable settings class for changing an app theme.

<AutoSnippet language="dart" {...whyImmutability}></AutoSnippet>

To use this code, remember to import `freezed_annotation`, add the part directive and run [build_runner] to generate the freezed classes!

[changenotifier]: https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html
[statenotifier]: https://pub.dev/documentation/riverpod/latest/riverpod/StateNotifier-class.html
[statenotifierprovider]: https://pub.dev/documentation/riverpod/latest/riverpod/StateNotifierProvider-class.html
[notifier]: https://pub.dev/documentation/riverpod/latest/riverpod/Notifier-class.html
[notifierprovider]: https://pub.dev/documentation/riverpod/latest/riverpod/NotifierProvider.html
[asyncvalue]: https://pub.dev/documentation/riverpod/latest/riverpod/AsyncValue-class.html
[freezed]: https://pub.dev/packages/freezed
[build_runner]: https://pub.dev/packages/build_runner
